/*Edition: par Taghrid Asfour le 15 novembre 2012*/
/*Vous pouvez utiliser ce code à condition de bien préciser le copyright Taghrid Asfour*/

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "structure.h"

#define SERVER_PORT 1500
#define CLIENT_PORT 1234
#define MAX_MSG 80


//////////////////////////////////////////

void init_messsage(paquet* mesg_){

	// construire un message en y insérant le numPaquet et les données
        if(mesg_==NULL) {
        mesg_= malloc(sizeof(paquet));
        }

        //On insert le numéro de block
        mesg_->numBlock=0;

        //On insert le numéro de séquence
        mesg_->numPaquet=0; 

        //Si le paquet est un ack il vaut 1
        mesg_->ack=0;

        //Si le paquet est un ack de type flux
        mesg_->flux=0;

        //Pour la gestion de congestion
        mesg_->flag=0;

	//On init la data a vide
	int y = 0 ;
	for(y=0;y<DATA;y++) mesg_->data[y]=' ' ;

}


void ecriture_fichier(paquet tab[]){

	//On ouvre le fichier
	FILE * fic ;
        fic = fopen("out.txt", "a+") ;
        if(fic == NULL){
                printf("Impossible d'ouvrir le fichier !");
                exit(1);
        } // fin if


	//On ecrit dans le fichier
	int i=0 ;
        for (i=0;i<8;i++){
                fputs(tab[i].data, fic);
        }


	//On ferme le fichier
	fclose(fic);
}

void init_file(){

	//On ouvre le fichier
	FILE * fic ;
        fic = fopen("out.txt", "w") ;
        if(fic == NULL){
                printf("Impossible d'ouvrir le fichier !");
                exit(1);
        } // fin if
        
	//On ferme le fichier
	fclose(fic);
}


///////////////////////////////////////////////////////////////////////


int main (int argc, char *argv[]) {
	
	//CtoS : communication Client => Serveur
	//StoC : communication Serveur => Client
	int sd_reception, n, sd_envoi, CtoS_senderLen;
	struct sockaddr_in CtoS_sender, CtoS_receiver, StoC_sender, StoC_receiver;
	
	// préparation pour la socket de reception
	paquet msg_recu, msg_temp, msg_ack, msg_vide, a_envoyer ;
	// Create and bind socket 
	if ((sd_reception = socket(PF_INET,SOCK_DGRAM, 0)) < 0) {
		perror(argv[0]);
		exit(1);
	}
	CtoS_receiver.sin_family = AF_INET;
	CtoS_receiver.sin_addr.s_addr = htonl(INADDR_ANY);
	CtoS_receiver.sin_port = htons(SERVER_PORT);
	if (bind(sd_reception, (struct sockaddr *)&CtoS_receiver, sizeof(CtoS_receiver)) < 0) {
		perror(argv[0]);
		exit(1);
	}
	
	// préparation pour la sockets d'envoi
	



	/* Create and bind socket */
	if ((sd_envoi = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
		perror(argv[0]);
		exit(1);
	}

	StoC_sender.sin_family = AF_INET;
	StoC_sender.sin_addr.s_addr = htonl(INADDR_ANY);
	StoC_sender.sin_port = htons(0);
	if (bind(sd_envoi,(struct sockaddr *)&StoC_sender, sizeof(StoC_sender)) < 0) {
		perror(argv[0]);
		exit(1);
	}

	/* Fill server address structure */
	StoC_receiver.sin_family = AF_INET;
	if (inet_aton("127.0.0.1", &(StoC_receiver.sin_addr))==0) {
		printf("%s: Invalid IP address format <%s>\n", argv[0], argv[1]);
		exit(1);
	}

	StoC_receiver.sin_port = htons(CLIENT_PORT);



///////////////////////////////////////////////////////////////////////

	//Variables
	int block_courant=0, cpt=0 ;
	paquet tab[8] ;
	
	// on initialise le fichier de sortie
	init_file() ;

	//Boucle d'écoute infinie avec traitement sérialisé des paquets
	for (;;) {
		CtoS_senderLen = sizeof(CtoS_sender);
		n = recvfrom(sd_reception, &msg_recu, MAX_MSG, 0,(struct sockaddr *)&CtoS_sender, (socklen_t*)&CtoS_senderLen);
		if (n<0) {
			perror(argv[0]);
			 exit(0);
		} else {
			
			// on vérifie en premier lieu si c'est un paquet de fin de transmission
			// si oui, on écrit le paquet en cours et on réinitialise le serveur
			if((msg_recu.ack==0)&&(msg_recu.flux==1)&&(msg_recu.flag==1)){
					// fin envoi. il faut remettre le serveur en écoute
					ecriture_fichier(tab) ;
					printf("#################################################\n") ;
					printf("Fin d'envoi : en attente de nouvelle connexion...\n") ;
					printf("#################################################\n") ;
						block_courant=0, cpt=0 ;
						init_messsage(&msg_vide);
						int x ; for (x=0;x<8;x++) tab[x] = msg_vide ;

					
					
			}
			else{

				printf("%s: received from %s (n° de block : %d, n° paquet : %d) :\n%s",argv[0], inet_ntoa(CtoS_sender.sin_addr),msg_recu.numBlock,msg_recu.numPaquet,msg_recu.data);
				printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n") ;
			
				//Si le numBlock du paquet reçu correspond au block courant, on l'enregistre dans la table
				// et on passe directement à la réception du suivant
				if (msg_recu.numBlock == block_courant){

					tab[msg_recu.numPaquet] = msg_recu ;
					cpt++ ;
				}
			
				// on est arrivé à la fin d'un block.
				// on stocke le paquet qui vient d'arriver (le premier du block suivant)
				//on choisit si on envoie un ACK positif ou negatif
				else{ 
				
					msg_temp = msg_recu ; // on stocke le msg du block suivant
					init_messsage(&msg_ack);
				
					// TEST d'intégralité du block
					if(cpt<8){
						// construction reponse qui va pas (ack=1, flag=1 : ack negatif)
						msg_ack.ack=1 ;
						msg_ack.flag=1 ;
						printf("Construction ACK negatif\n") ;
					}
					else{
						// envoi ack tt va bien (ACK positif)
						msg_ack.numBlock=block_courant;
						msg_ack.ack=1 ;
						printf("Construction ACK positif\n") ;
					}// fin else


				// on envoie l'ACK qu'on vient de construire
				if (sendto(sd_envoi, &msg_ack, sizeof(msg_ack), 0,(struct sockaddr *)&StoC_receiver, sizeof(StoC_receiver)) < 0){
					perror(argv[0]); exit(1);
				}
				




					// on écrit dans le fichier, même si le block n'est pas complet
					ecriture_fichier(tab) ;

				


					
					
					// reinitialiser la table
					init_messsage(&msg_vide);
					int z ; for (z=0;z<8;z++) tab[z] = msg_vide ;
				
					// ecrire le paquet temp dans la nouvelle table
					tab[msg_temp.numPaquet] = msg_temp ;
				
					// on met cpt à 1 (on a en effet stocké le premier paquet du block suivant)
					cpt=1 ;
					// incrementer block courant
					block_courant++ ;




					} // fin else
	
	
				}// fin else
		} // fin else
	} // fin for (boucle infinie)

close(sd_envoi) ;
close(sd_reception) ;
}


